
#include "gm_helper.h"
#include "pk.h"
#include "x509_crt.h"
#include "oid.h"

#include <stdlib.h>

static x509_crt g_cert;


static void reverse_bytes( void * input, long len )
{
	unsigned char swap;
	long i;
	unsigned char * pTmp = (unsigned char*)input;

	for (i=0; i<len/2; i++)
	{
		swap = pTmp[i];
		pTmp[i] = pTmp[len-i-1];
		pTmp[len-i-1] = swap;
	}
}



int _stdcall MKF_X509Der2RSAPRIVATEKEYBLOB(unsigned char * pData, int ulDataLen, RSAPRIVATEKEYBLOB * pRsaPriBlob)
{
	return parse_key_to_RSAPRIVATEKEYBLOB(pData, ulDataLen, pRsaPriBlob);
}

int _stdcall parse_key_to_RSAPRIVATEKEYBLOB(unsigned char * pData, int ulDataLen, RSAPRIVATEKEYBLOB * pRsaPriBlob)
{
	int rv = 0;
	pk_context ctx = {0};
	rsa_context * pRsa = NULL;
	int key_size = 0;
	int keyBytes = 0;
	int primeBytes =0;

	unsigned char modulus[512] = {0};
	unsigned char  prime1[256] = {0};
	unsigned char  prime2[256] = {0};
	unsigned char  primeExponent1[256] = {0};
	unsigned char  primeExponent2[256] = {0};
	unsigned char  coefficient[512] = {0};
	unsigned char  publicExponent[32]= {0};
	unsigned char  privateExponent[256] = {0};

	int N_len = 0;
	int P_len = 0;
	int Q_len = 0;
	int E_len = 0;
	int D_len = 0;
	int DP_len = 0;
	int DQ_len = 0;
	int QP_len = 0;

	pk_init(&ctx);

	if(0 != pk_parse_key(&ctx, pData, ulDataLen, NULL, 0))
	{
		return 1;
	}

	pRsa = pk_rsa(ctx);
	key_size = pk_get_size(&ctx);
	keyBytes = key_size / 8;
	primeBytes = keyBytes / 2;

	N_len = keyBytes; 
	mpi_write_binary(&pRsa->N, modulus, N_len);

	P_len = primeBytes;
	mpi_write_binary(&pRsa->P, prime1, P_len);

	Q_len = primeBytes;
	mpi_write_binary(&pRsa->Q, prime2, Q_len);

	E_len = MAX_RSA_EXPONENT_LEN;
	mpi_write_binary(&pRsa->E, publicExponent, E_len);

	D_len = keyBytes;
	mpi_write_binary(&pRsa->D, privateExponent, D_len);

	DP_len = primeBytes;
	mpi_write_binary(&pRsa->DP, primeExponent1, DP_len);

	DQ_len = primeBytes;
	mpi_write_binary(&pRsa->DQ, primeExponent2, DQ_len);

	QP_len = primeBytes;
	mpi_write_binary(&pRsa->QP, coefficient, QP_len);

	pk_free(&ctx);


	pRsaPriBlob->AlgID = SGD_RSA;
	pRsaPriBlob->BitLen = key_size;

	memcpy(pRsaPriBlob->Modulus + MAX_RSA_MODULUS_LEN - keyBytes,			modulus,	keyBytes);
	memcpy(pRsaPriBlob->Prime1 + MAX_RSA_MODULUS_LEN/2 - primeBytes,		prime1,		primeBytes);
	memcpy(pRsaPriBlob->Prime2 + MAX_RSA_MODULUS_LEN/2 - primeBytes,		prime2,		primeBytes);
	memcpy(pRsaPriBlob->Prime1Exponent + MAX_RSA_MODULUS_LEN/2 - primeBytes, primeExponent1, primeBytes);
	memcpy(pRsaPriBlob->Prime2Exponent + MAX_RSA_MODULUS_LEN/2 - primeBytes, primeExponent2, primeBytes);
	memcpy(pRsaPriBlob->Coefficient +  MAX_RSA_MODULUS_LEN/2 - primeBytes, coefficient, primeBytes);
	memcpy(pRsaPriBlob->PublicExponent + MAX_RSA_EXPONENT_LEN - E_len, publicExponent, E_len);
	memcpy(pRsaPriBlob->PrivateExponent+ MAX_RSA_MODULUS_LEN - keyBytes, privateExponent, keyBytes);

	return rv;
}

int parse_cert_attributes(unsigned char * pCertData, long cert_len)
{
	int ret = 0;

	x509_crt_free(&g_cert);
	x509_crt_init( &g_cert );
	
	ret = x509_crt_parse( &g_cert, pCertData, cert_len);
	if(0 != ret)
		return 1;

	parse_issuer_name(&ret);

	return 0;
}

void parse_cert_attributes_free()
{
	x509_crt_free(&g_cert);
}

unsigned char * parse_x509_string(const x509_name * name_x509, int * name_len)
{
#define NID_NAME "2.5.4.3"

	char oid_string[64] = {0};
	unsigned char oid[8] = {0};
	const x509_name * name = name_x509;
	while(name)
	{
		if( !name->oid.p )
		{
			name = name->next;
			continue;
		}

		oid_get_numeric_string(oid_string, 64, &name->oid);

		if(strcmp(oid_string, NID_NAME) == 0)
		{
			* name_len = name->val.len;
			return name->val.p;
		}

		name = name->next;
	}

	*name_len = 0;
	return  NULL;
}

unsigned char * parse_cert_get_serial_num(int * serial_len)
{
	* serial_len = g_cert.serial_raw.len;
	return g_cert.serial_raw.p; 
}

unsigned char * parse_cert_get_subject(int * subject_len)
{
	* subject_len = g_cert.subject_raw.len;
	return  g_cert.subject_raw.p;
}

//2.5.4.3-- COMMON NAME
unsigned char * parse_cert_get_CN(int * CN_len)
{
	return parse_x509_string(&g_cert.subject, CN_len);
}


unsigned char * parse_cert_get_label(int * label_len)
{
	unsigned char * pValue = parse_cert_get_CN(label_len);
	if(pValue)
		return pValue;

	* label_len = g_cert.subject.val.len;
	return g_cert.subject.val.p;
}

unsigned char * parse_cert_get_issuer(int * issuer_len)
{
	* issuer_len = g_cert.issuer_raw.len;
	return g_cert.issuer_raw.p;
}

unsigned char * parse_issuer_name(int * name_len)
{
	return parse_x509_string(&g_cert.issuer, name_len);
}

int parse_cert_get_start_date(unsigned char year[4],unsigned  char month[2],unsigned  char day[2])
{
	char tmp[32] = {0};
	sprintf(tmp, "%d", g_cert.valid_from.year);
	memcpy(year, tmp, 4);

	sprintf(tmp, "%d", g_cert.valid_from.mon);
	memcpy(month, tmp, 2);

	sprintf(tmp, "%d", g_cert.valid_from.day);
	memcpy(day, tmp, 2);

	return 0;
}

int parse_cert_get_end_date(unsigned char year[4],unsigned  char month[2],unsigned  char day[2])
{
	char tmp[32] = {0};
	sprintf(tmp, "%d", g_cert.valid_to.year);
	memcpy(year, tmp, 4);

	sprintf(tmp, "%d", g_cert.valid_to.mon);
	memcpy(month, tmp, 2);

	sprintf(tmp, "%d", g_cert.valid_to.day);
	memcpy(day, tmp, 2);

	return 0;
}

int get_public_key_from_cert(int ulCertType, unsigned char *pcbCert, int dwCertLen, 
							 unsigned char *pstPubKeyBlob, int *ulPubkeylen)
{
	int ret = 0;
	rsa_context *prsa = NULL;
	int pos = 0;
	unsigned char szN[256] = {0};
	unsigned char szE[4] = {0};
	int len = 0;
	unsigned char * p = NULL;

	x509_crt cacert;
	x509_crt_init( &cacert );

	ret = x509_crt_parse( &cacert, pcbCert, dwCertLen);
	if(0 != ret)
		return 1;

	if(ulCertType == 1)
	{
		prsa = (rsa_context *)cacert.pk.pk_ctx;
		pos = 0;
		mpi_write_binary(&prsa->N, szN, 256);
		mpi_write_binary(&prsa->E, szE, 4);

		len = prsa->len*8;
		memcpy(pstPubKeyBlob, &len, 4);
		reverse_bytes(pstPubKeyBlob, 4);
		pos += 4;
		memcpy(pstPubKeyBlob+pos, szN+256-prsa->len, prsa->len);
		pos += prsa->len;
		memcpy(pstPubKeyBlob+pos, szE, 4);
		pos += 4;
		*ulPubkeylen = pos;
	}
	else
	{
		p = (BYTE *)cacert.pk.pk_ctx;
		memcpy(pstPubKeyBlob,  p+4, 64);
	}

	x509_crt_free(&cacert);
	return 0;
}
